package com.acme.bankapp.domain.bank;

import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;

import com.acme.bankapp.domain.bank.exception.AccountAlreadyAssignedException;

/**
 * Bank client.
 * 
 */
public class Client {
	private Gender gender;

	private String name;

	private Set<Account> accounts = new LinkedHashSet<Account>();

	/**
	 * Creates new client.
	 * 
	 * @param gender
	 *            client's gender
	 * @param name
	 *            client's full name
	 */
	public Client(Gender gender, String name) {
		this.gender = gender;
		this.name = name;
	}

	/**
	 * Returns client salutation, i.e. "Mr. John Doe".
	 * 
	 * @return client salutation
	 */
	public String getClientSalutation() {
		return gender.toString() + " " + this.getName();
	}

	/**
	 * Returns all client accounts.
	 * 
	 * @return client accounts
	 */
	public Set<Account> getAccounts() {
		return new LinkedHashSet<Account>(accounts);
	}

	/**
	 * Assigns another account to the client.
	 * 
	 * @param account
	 *            account
	 * @throws AccountAlreadyAssignedException
	 */
	public void addAccount(Account account)
			throws AccountAlreadyAssignedException {
		String uuid = generateUUID();
		accounts.add(account);
		account.assignToClient(this, uuid);
	}

	/**
	 * Generates unique identifier for account (up to the client).
	 * 
	 * @return unique identifier
	 */
	private String generateUUID() {
		String uuid = UUID.randomUUID().toString();
		for (Account account : accounts) {
			if (account.getUUID().equals(uuid)) {
				return generateUUID();
			}
		}
		return uuid;
	}

	/**
	 * Returns client's gender.
	 * 
	 * @return client gender
	 */
	public Gender getGender() {
		return gender;
	}

	/**
	 * Returns client's name.
	 * 
	 * @return client name
	 */
	public String getName() {
		return name;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Client other = (Client) obj;
		if (gender != other.gender)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((gender == null) ? 0 : gender.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/**
	 * @inheritDoc
	 */
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("Client " + getClientSalutation() + ".");
		for (Account account : getAccounts()) {
			sb.append("\n-> " + account + ".");
		}
		return sb.toString();
	}
}